#!/usr/bin/env bash

set -x

update_repo() {
	local TARGET="${1}"
	local OUTPUT="$PWD/out/infra/$TARGET"
	local NEW_REPO="false"

	if [[ $RSDK_OPTION_DRY_RUN == "false" ]] && ! gh auth status &>/dev/null; then
		echo "This operation requires GitHub authentication:"
		if ! gh auth login; then
			error "$EXIT_AUTHENTICATION_FAILED" "github"
		fi
	fi

	if [[ -e "$OUTPUT/.git" ]] && (cd "$OUTPUT" && [[ -n $(git remote) ]]); then
		echo "Found existing repository. Updating..."
		pushd "$OUTPUT"
		if git remote show -n origin &>/dev/null; then
			if git fetch origin main; then
				git pull origin main
			else
				echo "Remote repository does not have 'main' branch. Continue..."
			fi
		fi
		popd
	else
		rm -rf "$OUTPUT"
		mkdir -p "$OUTPUT/.."
		echo "Trying to clone existing repository first..."
		if ! gh repo clone "$RSDK_TARGET_ORGANIZATION/$TARGET" "$OUTPUT" -- --single-branch; then
			if [[ $RSDK_OPTION_DRY_RUN == "true" ]]; then
				echo "Dry run. Will only create local repo."
				mkdir -p "$OUTPUT"
				pushd "$OUTPUT"
				git init
				popd
			else
				if [[ $RSDK_OPTION_NO_CONFIRM == "true" ]]; then
					echo "Target repository does not exist. New repository will be created."
				else
					read -rp "Target repository does not exist. Do you want to create it now? [y/N] "
					if [[ ${REPLY/Y/y} != "y" ]]; then
						echo "Operation cancelled."
						return 1
					fi
				fi
				pushd "$OUTPUT/.."
				gh repo create "$RSDK_TARGET_ORGANIZATION/$TARGET" --clone --public
				popd
				pushd "$OUTPUT"
				git branch -m main
				popd
			fi
			NEW_REPO="true"
		fi
	fi

	if [[ $RSDK_OPTION_PURGE_BEFORE_UPDATE == "true" ]]; then
		echo "Remove existing content..."
		local i file_exclusion=(
			"!" "-name" ".git"
		)
		for i in "${RSDK_GIT_REPO_KEEP_FILES[@]}"; do
			file_exclusion+=(
				"!" "-name" "$i"
			)
		done
		find "$OUTPUT" -mindepth 1 -maxdepth 1 "${file_exclusion[@]}" -exec rm -rf {} +
	else
		echo "Skip content removal."
	fi

	echo "Prepare folder structure..."
	pushd "$RSDK_GIT_REPO_TEMPLATE"
	find . -mindepth 1 -type d -exec mkdir -p "$OUTPUT/{}" \;
	popd

	echo "Populate content..."
	local RSDK_REV
	RSDK_REV="$(git rev-parse "HEAD^{commit}")"
	local JSONNET_ARGS=(
		"-S"
		"--tla-str" "target=$TARGET"
		"--tla-str" "build_org=$RSDK_BUILD_ORG"
		"--tla-str" "repo_org=$RSDK_APT_REPO_ORG"
		"--tla-str" "pkg_org=$RSDK_PACKAGE_ORG"
		"--tla-str" "git_rev=$RSDK_REV"
		"--tla-code" "new_repo=$NEW_REPO"
		"--multi" "$OUTPUT"
	)
	jsonnet "${JSONNET_ARGS[@]}" "$RSDK_GIT_REPO_TEMPLATE/template.jsonnet"

	if ! git diff --quiet; then
		RSDK_REV="$RSDK_REV.dirty"
	fi

	pushd "$OUTPUT"
	if [[ -n "$(git status --porcelain)" ]]; then
		git add .
		PRE_COMMIT_ALLOW_NO_CONFIG=1 git commit --no-verify --author "github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>" -m "chore: templates generated by RadxaOS-SDK/rsdk@$RSDK_REV"
	fi

	local description homepage="https://$RSDK_TARGET_ORGANIZATION.github.io/$TARGET/"
	description="$(repo_desc "$TARGET")"
	if [[ $RSDK_OPTION_DRY_RUN == "false" ]]; then
		git push --set-upstream origin main
		gh repo edit --description "$description" --homepage "$homepage"
		if [[ -n $RSDK_OPTION_RUN_WORKFLOW ]]; then
			gh workflow run "$RSDK_OPTION_RUN_WORKFLOW"
		fi
		if ! gh api --method POST -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" \
			"/repos/$RSDK_TARGET_ORGANIZATION/$TARGET/pages" -F "build_type=workflow"; then
			gh api --method PUT -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" \
				"/repos/$RSDK_TARGET_ORGANIZATION/$TARGET/pages" -F "build_type=workflow"
		fi
		if ! gh api --method POST -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" \
			"/repos/$RSDK_TARGET_ORGANIZATION/$TARGET/rulesets" --input "$SCRIPT_DIR/../../share/rsdk/common/repo_rulesets/default.json"; then
			local RULESET_ID
			RULESET_ID="$(gh api -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" \
				"/repos/$RSDK_TARGET_ORGANIZATION/$TARGET/rulesets" --jq '.[] | select(.name = "default").id')"
			gh api --method PUT -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" \
				"/repos/$RSDK_TARGET_ORGANIZATION/$TARGET/rulesets/$RULESET_ID" --input "$SCRIPT_DIR/../../share/rsdk/common/repo_rulesets/default.json"
		fi
	else
		echo "Set repository description as '$description'."
		echo "Set repository homepage as '$homepage'."
		echo "Set repository Pages build_type as 'workflow'."
	fi
	popd
}

main() {
	local SCRIPT_DIR BASE_NAME
	SCRIPT_DIR="$(dirname "$(realpath "$0")")"
	# shellcheck source=src/lib/rsdk/utils.sh
	source "$SCRIPT_DIR/../../lib/rsdk/utils.sh"
	# shellcheck source=src/lib/rsdk/stdlib.sh
	source "$SCRIPT_DIR/../../lib/rsdk/stdlib.sh"
	BASE_NAME="$(basename "$0")"

	local TEMP
	if ! TEMP="$(getopt -o "hds:tr:P:y" -l "help,dry-run,suffix:,test,run:,prefix:,yes" -n "$0" -- "$@")"; then
		return
	fi
	eval set -- "$TEMP"

	export RSDK_OPTION_DRY_RUN="${RSDK_OPTION_DRY_RUN:-false}"
	export RSDK_BUILD_ORG="${RSDK_BUILD_ORG:-$("$SCRIPT_DIR/../../bin/rsdk" config infra.organizations.product)}"
	export RSDK_APT_REPO_ORG="${RSDK_APT_REPO_ORG:-$("$SCRIPT_DIR/../../bin/rsdk" config infra.organizations.repository)}"
	export RSDK_PACKAGE_ORG="${RSDK_PACKAGE_ORG:-$("$SCRIPT_DIR/../../bin/rsdk" config infra.organizations.package)}"
	export RSDK_GIT_REPO_TYPE="${RSDK_GIT_REPO_TYPE:-}"
	export RSDK_GIT_REPO_ARRAY=("${RSDK_GIT_REPO_ARRAY[@]}")
	export RSDK_GIT_REPO_TEMPLATE="${RSDK_GIT_REPO_TEMPLATE:-}"
	export RSDK_TARGET_SUFFIX="${RSDK_TARGET_SUFFIX:-}"
	export RSDK_TARGET_ORGANIZATION="${RSDK_TARGET_ORGANIZATION:-}"
	export RSDK_GIT_REPO_KEEP_FILES=("${RSDK_GIT_REPO_KEEP_FILES[@]}")
	export RSDK_OPTION_RUN_WORKFLOW="${RSDK_OPTION_RUN_WORKFLOW:-}"
	export RSDK_TARGET_PREFIX="${RSDK_TARGET_PREFIX:-}"
	export RSDK_OPTION_NO_CONFIRM="${RSDK_OPTION_NO_CONFIRM:-false}"
	export RSDK_OPTION_PURGE_BEFORE_UPDATE="${RSDK_OPTION_PURGE_BEFORE_UPDATE:-true}"

	# `gh`` will block execution if stdout cannot fit all content
	export GH_PAGER=

	case "$BASE_NAME" in
	"rsdk-infra-update")
		echo "'$BASE_NAME' cannot be run directly." >&2
		echo "Please run one of its symbolic links instead." >&2
		return 1
		;;
	"rsdk-infra-package-update")
		RSDK_GIT_REPO_TYPE="package"
		RSDK_GIT_REPO_TEMPLATE="$SCRIPT_DIR/../../share/rsdk/infra-package"
		RSDK_TARGET_ORGANIZATION="$RSDK_PACKAGE_ORG"
		repo_desc() {
			local desc
			desc="$(gh repo view "$RSDK_TARGET_ORGANIZATION/$1" --json description --jq '.description')"
			echo "${desc:-$1}"
		}
		RSDK_OPTION_PURGE_BEFORE_UPDATE="false"
		;;
	"rsdk-infra-product-update")
		RSDK_GIT_REPO_TYPE="product"
		mapfile -t RSDK_GIT_REPO_ARRAY < <(jq -er '.[] | select(.override_product == null).product' "$SCRIPT_DIR/../../share/rsdk/configs/products.json") && array_remove "RSDK_GIT_REPO_ARRAY" ""
		RSDK_GIT_REPO_TEMPLATE="$SCRIPT_DIR/../../share/rsdk/infra-product"
		RSDK_TARGET_ORGANIZATION="$RSDK_BUILD_ORG"
		repo_desc() {
			jq -er --arg target "$1" '.[] | select(.product == $target).product_full_name' "$SCRIPT_DIR/../../share/rsdk/configs/products.json"
		}
		;;
	"rsdk-infra-repo-update")
		RSDK_GIT_REPO_TYPE="suite"
		mapfile -t RSDK_GIT_REPO_ARRAY < <(jq -er '.[].supported_suites[]' "$SCRIPT_DIR/../../share/rsdk/configs/distributions.json") && array_remove "RSDK_GIT_REPO_ARRAY" ""
		RSDK_GIT_REPO_TEMPLATE="$SCRIPT_DIR/../../share/rsdk/infra-repo"
		RSDK_TARGET_ORGANIZATION="$RSDK_APT_REPO_ORG"
		RSDK_GIT_REPO_KEEP_FILES=(
			"pkgs.json"
			"pkgs.lock"
		)
		repo_desc() {
			local distro type target="$1"
			target="${target%-test}"
			case "$1" in
			*-test)
				type="Radxa Test Repository"
				;;
			*)
				type="Radxa Repository"
				;;
			esac
			distro="$(jq -er --arg target "$target" '.[] | select(.suites[] | . == $target).distribution' "$SCRIPT_DIR/../../share/rsdk/configs/distributions.json")"
			echo "$type for $distro $target"
		}
		;;
	*)
		echo "Unsupported command '$BASE_NAME'." >&2
		return 1
		;;
	esac

	while true; do
		TEMP="$1"
		shift
		case "$TEMP" in
		-d | --dry-run)
			RSDK_OPTION_DRY_RUN="true"
			;;
		-h | --help)
			TEMP="$(basename "$0")"
			rsdk help "${TEMP#rsdk-}"
			return
			;;
		-s | --suffix)
			RSDK_TARGET_SUFFIX="$1"
			shift
			;;
		-t | --test)
			RSDK_TARGET_SUFFIX="-test"
			;;
		-r | --run)
			RSDK_OPTION_RUN_WORKFLOW="$1"
			shift
			;;
		-P | --prefix)
			RSDK_TARGET_PREFIX="$1"
			shift
			;;
		-y | --yes)
			RSDK_OPTION_NO_CONFIRM="true"
			;;
		--)
			break
			;;
		*)
			error "$EXIT_UNKNOWN_OPTION" "$TEMP"
			;;
		esac
	done

	if ! git merge-base --is-ancestor HEAD "@{u}"; then
		echo "Current HEAD is not present in the remote." >&2
		echo "Did you push your local branch?" >&2
		echo "Abort due to potential non-working ref assignment." >&2
		return 1
	fi

	if (($# == 0)); then
		echo "No $RSDK_GIT_REPO_TYPE supplied."
		echo "============="
		echo "Currently, following $RSDK_GIT_REPO_TYPE are supported:"
		printf "${RSDK_TARGET_PREFIX}%s${RSDK_TARGET_SUFFIX}\n" "${RSDK_GIT_REPO_ARRAY[@]}"
		read -rp "Do you want to update ALL supported ${RSDK_GIT_REPO_TYPE}s? [y/N] "
		if [[ ${REPLY/Y/y} != "y" ]]; then
			echo "Operation cancelled."
			return 1
		fi
		set -- "${RSDK_GIT_REPO_ARRAY[@]}"
	fi

	while (($# != 0)); do
		update_repo "${RSDK_TARGET_PREFIX}${1}${RSDK_TARGET_SUFFIX}"
		shift
	done

	echo "============="
	echo "Operation completed."
	echo "If a new repo is created, please remember to check:"
	echo "  * organization secret setting"
	echo "  * and PAT authorization (if auto repo update is enabled)."
}

main "$@"
